package com.lavender.circle_dependency;


import com.lavender.circle_dependency.setter_dependency_prototype.SetterPrototypeA;
import com.lavender.circle_dependency.setter_dependency_prototype.SetterPrototypeB;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 * 参考：https://blog.csdn.net/f641385712/article/details/92801300?ops_request_misc=%25257B%252522request%25255Fid%252522%25253A%252522160881048416780271156469%252522%25252C%252522scm%252522%25253A%25252220140713.130102334.pc%25255Fall.%252522%25257D&request_id=160881048416780271156469&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~hot_rank-1-92801300.nonecase&utm_term=%E5%BE%AA%E7%8E%AF%E4%BE%9D%E8%B5%96
 * 鲁班子路：https://blog.csdn.net/java_lyvee/article/details/101793774
 * Spring解决循环依赖的实现类：{@link org.springframework.beans.factory.support.DefaultSingletonBeanRegistry}
 *
 * 一：Spring循环依赖场景演示：
 * 1：构造器注入。Spring无法解决该种方式的循环依赖
 * {@link com.lavender.circle_dependency.construct_dependency.ConstructA}
 * {@link com.lavender.circle_dependency.construct_dependency.ConstructB}
 *
 * 2：setter(单例singleton)注入。Spring能解决该种方式的循环依赖
 * {@link com.lavender.circle_dependency.setter_dependency_singleton.SetterSingletonA}
 * {@link com.lavender.circle_dependency.setter_dependency_singleton.SetterSingletonB}
 *
 * 3：setter(原型prototype)注入。
 * # 真prototype(prototype设置生效)。Spring能解决该种方式的循环依赖
 * @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)：每次调用均会生成一个新Bean。（@Autowired的代理对象，因此不会被影响）
 * # 伪prototype(prototype设置不生效)。Spring无法解决该种方式的循环依赖
 * @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)：每次调用不会生成一个新Bean。
 * 注：启动时是不会报错的（因为非单例Bean默认不会初始化，而是使用时才会初始化），所以很简单咱们只需要手动getBean()或者在一个单例Bean内@Autowired即可
 * {@link SetterPrototypeA}
 * {@link SetterPrototypeB}
 *
 * 补充知识点：
 * 对Bean设置prototype类型时，需要设置proxyMode，否则prototype设置无效。
 * @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)：多例有效
 * @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)：多例无效
 *
 * 二：工作中如何避免
 * 1：尽量使用setter注入。
 *
 * 三：当Spring aop遇上循环依赖
 * 参考：https://blog.csdn.net/chaitoudaren/article/details/105060882
 *
 * 面试题：（https://javazhiyin.blog.csdn.net/article/details/107602783）
 * 面试官：”Spring是如何解决的循环依赖？“
 * 答：Spring通过三级缓存解决了循环依赖，其中一级缓存为单例池（singletonObjects），二级缓存为早期曝光对象earlySingletonObjects，
 * 三级缓存为早期曝光对象工厂（singletonFactories）。当A、B两个类发生循环引用时，在A完成实例化后，就使用实例化后的对象去创建一个对象工厂，并添加到三级缓存中，
 * 如果A被AOP代理，那么通过这个工厂获取到的就是A代理后的对象，如果A没有被AOP代理，那么这个工厂获取到的就是A实例化的对象。
 * 当A进行属性注入时，会去创建B，同时B又依赖了A，所以创建B的同时又会去调用getBean(a)来获取需要的依赖，此时的getBean(a)会从缓存中获取，
 * 第一步，先获取到三级缓存中的工厂；第二步，调用对象工工厂的getObject方法来获取到对应的对象，得到这个对象后将其注入到B中。
 * 紧接着B会走完它的生命周期流程，包括初始化、后置处理器等。当B创建完后，会将B再注入到A中，此时A再完成它的整个生命周期。至此，循环依赖结束！
 *
 * 面试官：”为什么要使用三级缓存呢？二级缓存能解决循环依赖吗？“
 * 答：如果要使用二级缓存解决循环依赖，意味着所有Bean在实例化后就要完成AOP代理，这样违背了Spring设计的原则，
 * Spring在设计之初就是通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来在Bean生命周期的最后一步来完成AOP代理，而不是在实例化后就立马进行AOP代理。
 *
 *
 * 二级缓存的作用：提高加载效率，避免循环依赖中再次通过工厂获取bean这一复杂流程，提升加载效率。
 * 三级缓存的作用：singletonFactories，用于解决AOP
 *
 * </p>
 * @author: zhu.chen
 * @date: 2020/12/24
 * @version: v1.0.0
 */
@RestController
public class DependencyTest {

    @Autowired
    private SetterPrototypeA setterPrototypeA;

    @Autowired
    private SetterPrototypeB setterPrototypeB;

    @GetMapping("/hello")
    public String hello() {
        System.out.println("setterPrototypeA : " + setterPrototypeA);
        System.out.println("setterPrototypeB : " + setterPrototypeB);
        return "ok";
    }

}
